﻿using System;
using System.Collections.Generic;
using UnityEngine;
using System.Diagnostics;
using Debug = UnityEngine.Debug;

namespace PathFinding
{
    public class AStarSeeker : ISeeker
    {
        private Grid grid;
        public AStarSeeker(Grid grid)
        {
            this.grid = grid;
        }

        //更快的contains判断
        private HashSet<Cell> closeSet = new HashSet<Cell>();
        private HashSet<Cell> openSet = new HashSet<Cell>();
        private Cell orgCell;
        private Cell endCell;

        public List<Cell> findPath(Vector2 startPos, Vector2 endPos)
        {
            //startPos
            int startX = this.grid.getCol(startPos.x);
            int startY = this.grid.getRow(startPos.y);
            int endX = this.grid.getCol(endPos.x);
            int endY = this.grid.getRow(endPos.y);
            if (!this.grid.isInBound(startX, startY) || !this.grid.isInBound(endX, endY))
            {
                Debug.LogError("起点or终点 不在格子内");
                return null;
            }
            Stopwatch stopwatch = new Stopwatch();
            stopwatch.Start();
            orgCell = this.grid[startX, startY];
            endCell = this.grid[endX, endY];

            closeSet.Clear();
            openSet.Clear();

            orgCell.parent = null;
            closeSet.Add(orgCell);

            Cell currCell = orgCell;
            while (currCell != endCell)
            {
                getOpenSet(currCell);
                if (openSet.Count <= 0)
                {
                    Debug.LogError("A*寻路失败");
                    return null;
                }
                currCell = findMinCostCell();
                closeSet.Add(currCell);
                openSet.Remove(currCell);
            }

            List<Cell> path = new List<Cell>();
            Cell loopCell = endCell;
            while (loopCell != null)
            {
                path.Add(loopCell);
                loopCell = loopCell.parent;
            }
            path.Reverse();
            stopwatch.Stop();
            Debug.LogError("AStar寻路开销 " + stopwatch.ElapsedMilliseconds);
            return path;
        }

        private Cell findMinCostCell()
        {
            int minF = int.MaxValue;
            Cell result = null;
            var ier = openSet.GetEnumerator();
            while (ier.MoveNext())
            {
                Cell point = ier.Current;
                int F = point.gCost + point.hCost;
                if (F < minF)
                {
                    minF = F;
                    result = point;
                }
            }
            return result;
        }

        private void getOpenSet(Cell currCell)
        {
            for (int x = -1; x <= 1; x++)
            {
                for (int y = -1; y <= 1; y++)
                {
                    if (x != 0 || y != 0)
                    {
                        int nextX = currCell.x + x;
                        int nextY = currCell.y + y;
                        if (this.grid.isInBound(nextX, nextY))
                        {
                            Cell nextCell = this.grid[nextX, nextY];
                            bool isLinePath = x == 0 || y == 0;
                            bool canCross = true;
                            if (!isLinePath)
                            {
                                canCross = getIsCanCross(nextX, nextY, x, y);
                            }
                            if (canCross && nextCell.isWalkable)
                            {
                                if (!closeSet.Contains(nextCell))
                                {
                                    int G = isLinePath ? 10 : 14;
                                    G += currCell.gCost;
                                    if (!openSet.Contains(nextCell))
                                    {
                                        nextCell.parent = currCell;
                                        nextCell.gCost = G;
                                        nextCell.hCost = (Mathf.Abs(nextCell.x - endCell.x) + Mathf.Abs(nextCell.y - endCell.y)) * 10;
                                        openSet.Add(nextCell);
                                    }
                                    else
                                    {
                                        if (G < nextCell.gCost)
                                        {
                                            nextCell.parent = currCell;
                                            nextCell.gCost = G;
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }

        private bool getIsCanCross(int x, int y, int dirX, int dirY)
        {
            int horX = x - dirX;
            int horY = y - dirY;
            //可以走斜角
            //return grid.isInBound(x, y) && grid[x, y].isWalkable && ((grid.isInBound(horX, y) && grid[horX, y].isWalkable) || (grid.isInBound(x, horY) && grid[x, horY].isWalkable));
            //不能走斜角
            return grid.isInBound(x, y) && grid[x, y].isWalkable && grid.isInBound(horX, y) && grid[horX, y].isWalkable && grid.isInBound(x, horY) && grid[x, horY].isWalkable;
        }

        private Cell getNeighbor(int currX, int currY, int x, int y)
        {
            x += currX;
            y += currY;
            if (this.grid.isInBound(x, y))
            {
                return this.grid[x, y];
            }
            return null;
        }


    }

}
